#include <windows.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#define N 20

void BeginPrint(char Map[N][N], HANDLE hBuffer)
{
    //定义 CHAR_INFO 缓冲区（存储字符 + 颜色）
    CHAR_INFO charBuffer[N + 2][2*N + 2];

    //填充 CHAR_INFO 缓冲区
    //颜色内容：边框：绿色
    //          背景：白色
    //          蛇：黄色
    //          食物：红色
    //上边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[0][i].Char.AsciiChar = '-';
		charBuffer[0][i].Attributes = FOREGROUND_GREEN;
		
	}
	//地图内容
    for (int i = 0; i < N; i++)
	{
        //左边界
        charBuffer[i+1][0].Char.AsciiChar = '|';
        charBuffer[i+1][0].Attributes = FOREGROUND_GREEN;
		
        //地图内容
        for(int j = 0; j < N; j++)
		{
            charBuffer[i+1][2*j+1].Char.AsciiChar = Map[i][j];
            if (Map[i][j] == '@' || Map[i][j] == '*')
                charBuffer[i+1][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else if (Map[i][j] == 'O')
                charBuffer[i+1][2*j+1].Attributes = FOREGROUND_RED | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else
            	charBuffer[i+1][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            charBuffer[i+1][2*j+2].Char.AsciiChar = ' ';
            charBuffer[i+1][2*j+2].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
			                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
        }
        //标语
		if(i == 9)
		{
			charBuffer[i+1][N/2 + 4].Char.AsciiChar = 'W';
			charBuffer[i+1][N/2 + 4].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			charBuffer[i+1][N/2 + 6].Char.AsciiChar = 'e';
			charBuffer[i+1][N/2 + 6].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			charBuffer[i+1][N/2 + 8].Char.AsciiChar = 'l';
			charBuffer[i+1][N/2 + 8].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			charBuffer[i+1][N/2 + 10].Char.AsciiChar = 'c';
			charBuffer[i+1][N/2 + 10].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			charBuffer[i+1][N/2 + 12].Char.AsciiChar = 'o';
			charBuffer[i+1][N/2 + 12].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			charBuffer[i+1][N/2 + 14].Char.AsciiChar = 'm';
			charBuffer[i+1][N/2 + 14].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			charBuffer[i+1][N/2 + 16].Char.AsciiChar = 'e';
			charBuffer[i+1][N/2 + 16].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
		}
        //右边界
        charBuffer[i+1][2*N+1].Char.AsciiChar = '|';
		charBuffer[i+1][2*N+1].Attributes = FOREGROUND_GREEN;
    }
    //下边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[N+1][i].Char.AsciiChar = '-';
		charBuffer[N+1][i].Attributes = FOREGROUND_GREEN;
	}

    COORD bufferSize = {2*N + 2, N + 2};    //定义写入区域
    COORD bufferCoord = {0, 0};      //从 CHAR_INFO 缓冲区的 (0,0) 开始读取
    SMALL_RECT writeRegion = {0, 0, 2*N + 1, N + 1};   //控制台写入区域

    //使用双缓冲一次性写入字符和颜色
    WriteConsoleOutput(
        hBuffer,    //控制台的屏幕缓冲区（直接输出的缓冲区）
        (CHAR_INFO*)charBuffer,     // CHAR_INFO 缓冲区（预先准备的写入了字符和颜色的缓冲区）
        bufferSize,    //写入的区域
        bufferCoord,    //读取起始点
        &writeRegion    //控制台写入区域
    );

    //重置光标
    SetConsoleCursorPosition(hBuffer, (COORD){0, 0});
}

void BeginLogic(HANDLE hBuffer)
{
    //定义并清洗地图
    char Map[N][N];
    for(int i = 0; i < N; i++)
	{
		for(int j = 0; j < N; j++)
		{
			Map[i][j] = ' ';
		}
	}
    //定义蛇的长度及所有部位的坐标
    int length = N/2, snake[N*N][2];
	//定义初始蛇各个部位的坐标
    for(int i = 0; i < N/2; i++)
    {
        snake[i][0] = N/4 + 2; snake[i][1] = N/4 + i;
        Map[snake[i][0]][snake[i][1]] = '*';
    }
    //定义食物的坐标
    int x = N/2 - 2, y = 3*N/4, direction = 2;
    Map[x][y] = 'O';
    //划归蛇头的位置
	Map[snake[0][0]][snake[0][1]] = '@';
	
    //打印地图
	BeginPrint(Map, hBuffer);
	Sleep(500);
    while (1)
	{
        //蛇的变化：将尾巴置为空格，所有部位向前移动一个，覆盖掉原本头的位置
        Map[snake[length-1][0]][snake[length-1][1]] = ' ';
        for(int i = length-1; i > 0; i--)
		{
			snake[i][0] = snake[i-1][0];
			snake[i][1] = snake[i-1][1];
		}
        //根据存储的方向确定头的下一个位置
        switch (direction)
        {
        case 1:
            snake[0][0]--;
            break;
        case 2:
            snake[0][1]--;
            break;
        case 3:
            snake[0][0]++;
            break;
        case 4:
            snake[0][1]++;
            break;
        default:
            break;
        }
        //检测是否吃到食物
        if (Map[snake[0][0]][snake[0][1]] == 'O')
		{
            //初始界面1
            Map[snake[0][0]][snake[0][1]] = '@';
			Map[snake[1][0]][snake[1][1]] = '*';
			BeginPrint(Map, hBuffer);
            Sleep(1000);
            return;
		}
		//重新划归蛇的位置,划归下一个蛇头，以及将接替蛇头的位置的蛇头划归为蛇身
    	Map[snake[0][0]][snake[0][1]] = '@';
		Map[snake[1][0]][snake[1][1]] = '*';
		//打印地图
        BeginPrint(Map, hBuffer);
        //按照规定改变方向
        if(snake[0][0] == N/4 + 2  && snake[0][1] == N/4 - 1)
            direction = 3;
        if(snake[0][0] == 3*N/4 - 4 && snake[0][1] == N/4 - 1)
            direction = 4;
        if(snake[0][0] == 3*N/4 - 4 && snake[0][1] == 3*N/4)
            direction = 1;
        Sleep(150);
    }
}

int main()
{
	//定义双缓冲的句柄
	HANDLE hBuffer = CreateConsoleScreenBuffer(
	    GENERIC_READ | GENERIC_WRITE,	//缓冲区设置为可读（输出）可写（改变状态）
	    FILE_SHARE_READ | FILE_SHARE_WRITE,
	    NULL,	//默认安全属性
	    CONSOLE_TEXTMODE_BUFFER,	//应为程序由字符呈现，故采用文本模式的缓冲区
	    NULL	//无模板
	);
	SetConsoleActiveScreenBuffer(hBuffer); 		//设置为活动缓冲区
	
	BeginLogic(hBuffer);
	
    //释放资源
    SetConsoleActiveScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE));
	CloseHandle(hBuffer);
    printf("程序结束\n");
    return 0;
}